#!/usr/bin/env python
#-*-coding:utf-8-*-
# ---------------------------------
# version:  1.0
#
# create-time:      <2008/12/03 02:50:04>
# last-update-time: <halida 12/21/2008 21:11:13>
# ---------------------------------
# module : entity
# the base of powergtd

from qtlib import *
from datetime import *
import pickle
from xml.dom.minidom import parse,parseString

import config

#consts
POOL_CHANGED = "poolChanged"
ALL = u"all"
NOW = u"now"
LATER = u"later"
DONE = u"done"
REFERENCE = u"reference"
STATUS = (NOW,LATER,DONE,REFERENCE)
PLACES = ALL,u"office",u"home",u"outside"

class Entity():
    """entity is the atom of thoughts."""
    def __init__(self,name=""):
        self.id = id()#uuid
        self.name = name
        self.desc = ""
        #time
        self.createTime = datetime.now()
        self.updateTime = datetime.now()
        #other
        self.tags = set()
        self.level = 1
        #schedule
        self.scheduled = False
        self.nextTime = datetime.now()
        self.looped = False
        self.loopCondition = None
        self.lastTime = datetime(3000,1,1)
        #relations
        self.parents = set()
        self.children = set()

    def checkData(self):
        """
        there are always new data, 
        so old entity need to add them.
        """
        try:
            self.looped
        except:
            self.looped = False
        try:
            self.loopCondition
        except:
            self.loopCondition = None
                

    def set(self,**kw):
        #set key
        for key in kw:
            self.__dict__[key]=kw[key]
        self.updateTime = datetime.now()

    def data(self):
        data = []
        for key in self.__dict__:
            info = key + ":\n" + unicode(self.__dict__[key])
            data.append(info)
        return "\n".join(data)

    #---relations---
    def addParent(self,entity):
        self.parents.add(entity.id)
        entity.children.add(self.id)

    def addChild(self,entity):
        self.children.add(entity.id)
        entity.parents.add(self.id)

    def removeParent(self,entity):
        self.parents.remove(entity.id)
        entity.children.remove(self.id)

    def removeChild(self,entity):
        self.children.remove(entity.id)
        entity.parents.remove(self.id)

    def hasParent(self,entity):
        return entity.id in self.parents

    def hasChild(self,entity):
        return entity.id in self.children

    #---tag---
    def addTag(self,tag):
        #status unique
        if tag in STATUS:
            map(lambda t:self.removeTag(t),
                STATUS)
        self.tags.add(tag)

    def addTags(self,tags):
        for tag in tags:
            self.addTag(tag)

    def hasTag(self,tag):
        return tag in self.tags

    def hasTags(self,tags):
        return tags.issubset(self.tags)

    def removeTag(self,tag):
        try:
            self.tags.remove(tag)
        except:
            pass

    #---level---
    #level uses eval priority of entity
    def levelUp(self):
        self.level+=1
    def levelDown(self):
        self.level-=1

    #---schedule---
    def checkScheduled(self,now):
        #check
        if not self.scheduled:return
        if self.lastTime<now:return
        if self.nextTime>now:return
        #update
        self.addTag(NOW)
        self.scheduled=False
        logging.info("scheduled: %s",self.name)
        return True

    def checkLoop(self,now):
        #loop has a end day
        logging.debug("looping checking.. ")
        if now > self.lastTime:
            self.looped = False
            return
        #if current time fit loop condition,
        #then this entity is due.
        if not self.loopCondition:return
        logging.debug("looping check:%s, %s",self.name,str(self.loopCondition))
        for key,value in self.loopCondition.iteritems():
            if value == []:continue
            if key == 'hour':
                if not now.hour in value:return
            elif key == 'day':
                if not now.day in value:return
            elif key == 'month':
                if not now.month in value:return
            elif key == 'weekday':
                if not now.weekday() in value:return
            elif key == 'minute':
                if not now.day in value:return
        #update
        self.addTag(NOW)
        logging.info("looped: %s",self.name)
        return True                

    #---XML---
    def saveToXML(self,dom):
        node = dom.createElement("entity")
        for key,value in self.__dict__.iteritems():
            logging.debug("entity to xml:%s\t:\t%s",key,unicode(value))
            keynode = dom.createElement(key)
            node.setAttribute(key,unicode(value))
        logging.debug(unicode(self.desc))
        dom.documentElement.appendChild(node)

    def loadFromXML(self,node):
        #todo
        pass

class EntityPool(QObject):
    """
    the entity container.
    """
    def __init__(self):
        QObject.__init__(self)
        self.pool = set()
        self.file = config.get("pool")
        self.load()
        #conn(self,"destroyed(QObject*)",self.save)

    def add(self,name):
        entity = Entity(name) 
        self.pool.add(entity)
        self.changed()
        return entity

    def get(self,id=None,func=None):
        #get all
        if id==None and func==None:
            return self.pool
        #get entity by id
        if id!=None :
            func = lambda e:e.id==id
            result = filter(func,self.pool)
            if result==[]:
                return None
            else:
                return result[0]
        else:
            return filter(func,self.pool)

    def set(self,id,**kw):
        entity = self.get(id)
        if entity==None:return
        elif kw!={}:
            entity.set(**kw)
            emit(self,POOL_CHANGED,entity.id)

    def remove(self,id):
        entity = self.get(id)
        if entity ==None:return
        self.pool.remove(entity)

    def changed(self):
        """if pool changed, this event will be called."""
        emit(self,POOL_CHANGED,())

    #---presistence---
    def save(self):
        output = open(self.file,'wb')
        pickle.dump(self.pool,output)
        output.close()
        logging.info("entity pool saving..")

    def load(self):
        if not os.path.isfile(self.file):
            return
        output = open(self.file,'rb')
        self.pool = pickle.load(output)
        output.close()
        #check data
        for e in self.pool:
            e.checkData()
        logging.info("entity pool loading..")
        

    INIT_XML = """<?xml version="1.0" ?>
<EntityPool/>
"""
    def saveToXML(self,file):
        #create dom
        dom = parseString(EntityPool.INIT_XML)
        #parse
        for entity in self.pool:
            entity.saveToXML(dom)
        #save
        p = open(file,'w')
        xmlstring = dom.toprettyxml()
        logging.debug(xmlstring)
        p.write(xmlstring.encode('utf-8'))
        p.close()        

    def loadFromXML(self,file):
        #todo
        pass


    #---entity process---
    def split(self,entity):
        if not entity in self.pool:
            raise Exception("this pool not have entity:"+entity.name)
        for line in unicode(entity.desc).splitlines():
            line = line.strip()
            if line == "": continue
            new_entity = self.add(line)
            new_entity.addParent(entity)
            new_entity.addTags(entity.tags)

        
        
